/**
 *    Copyright (C) 2009 Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_API_METADATA_FIELD_HPP
#define _CUBEFS_CORE_API_METADATA_FIELD_HPP

#include <cubefs/api/metadata/field.hpp>
#include <cubefs/core/api/metadata/metadata.hpp>

#include <cubefs/core/tools/types/dispatcher.hpp>
#include <cubefs/core/tools/bitmap.hpp>

#define CUBEFS_SOURCE "cubefs/core/api/metadata/field.hpp:" CUBEFS_AT

namespace cubefs { namespace api {

using namespace cubefs::tools;

template <typename Interface>
class FieldMetadataImplT: public MetadataImplT<Interface> {
	typedef FieldMetadataImplT<Interface> 	Me;
	typedef MetadataImplT<Interface> 		Base;

	typedef typename Interface::Code2TypeMap Code2TypeMap;

public:

	FieldMetadataImplT(Int ptr, Int abi_ptr, Byte type, const string &name, const Int offset, const Int limit, Int item_size) :
		Base(name, type), ptr_(ptr), abi_ptr_(abi_ptr), offset_(offset), limit_(limit), item_size_(item_size) {
		SetupValueType();
	}

	FieldMetadataImplT(Int ptr, Int abi_ptr, Byte type, const string &name, const Int limit, Int item_size) :
		Base(name, type), ptr_(ptr), abi_ptr_(abi_ptr), offset_(0), limit_(limit), item_size_(item_size) {
		SetupValueType();
	}

	FieldMetadataImplT(Int ptr, Int abi_ptr, Byte type, const string &name, Int item_size) :
		Base(name, type), ptr_(ptr), abi_ptr_(abi_ptr), offset_(0), limit_(item_size), item_size_(item_size) {
		SetupValueType();
	}

	virtual Int Ptr() const {
		return ptr_;
	}

	virtual Int AbiPtr() const {
		return abi_ptr_;
	}

	virtual Int Count() const {
		return (limit_ - offset_) / Size();
	}

	virtual Int Size() const {
		return item_size_;
	}

	virtual Int Offset() const {
		return offset_;
	}

	virtual Int Limit() const {
		return limit_;
	}

	virtual const void *ValuePtr(const void *mem, Int idx) const {
		return ValuePtr(mem, idx, ptr_);
	}

	virtual void *ValuePtr(void *mem, Int idx) {
		return ValuePtr(mem, idx, ptr_);
	}

	virtual const void *ValuePtrAbi(const void *mem, Int idx) const {
		return ValuePtr(mem, idx, abi_ptr_);
	}

	virtual void *ValuePtrAbi(void *mem, Int idx) {
		return ValuePtr(mem, idx, abi_ptr_);
	}

	virtual const void *BitmapPtr(const void *mem) const {
		return BitmapPtr(mem, ptr_);
	}

	virtual void *BitmapPtr(void *mem) {
		return BitmapPtr(mem, ptr_);
	}

	virtual const void *BitmapPtrAbi(const void *mem) const {
		return BitmapPtr(mem, abi_ptr_);
	}

	virtual void *BitmapPtrAbi(void *mem) {
		return BitmapPtr(mem, abi_ptr_);
	}

	virtual void SetValue(void *mem, const void *value, Int idx) {
		return SetValue(mem, value, idx, ptr_);
	}

	virtual void SetValueAbi(void *mem, const void *value, Int idx) {
		return SetValue(mem, value, idx, abi_ptr_);
	}

	virtual void GetValue(const void *mem, void *value, Int idx) const {
		return GetValue(mem, value, idx, ptr_);
	}

	virtual void GetValueAbi(const void *mem, void *value, Int idx) const {
		return GetValue(mem, value, idx, abi_ptr_);
	}

	virtual BigInt GetBits(const void *mem, Int idx, Int nbits) const {
		return GetBits(mem, idx, nbits, ptr_);
	}

	virtual BigInt GetBitsAbi(const void *mem, Int idx, Int nbits) const {
		return GetBits(mem, idx, nbits, abi_ptr_);
	}

	virtual void SetBits(void *mem, BigInt bits, Int idx, Int nbits) {
		SetBits(mem, bits, idx, nbits, ptr_);
	}

	virtual void SetBitsAbi(void *mem, BigInt bits, Int idx, Int nbits) {
		SetBits(mem, bits, idx, nbits, abi_ptr_);
	}

	virtual Typed* CreateValueHolder();

	virtual Type* GetValueType() {
		return valueType_;
	}

    static void Init() {
    	AbstractType* type = new AbstractType(TypeNameFactory<Interface>::name(), Metadata::MyType());
    	type_ = type;

    	type->AddOperation(new BasicOperation<Int (Me::*)(), Int (Me::*)() const>(type, "Ptr", &Me::Ptr));
    	type->AddOperation(new BasicOperation<Int (Me::*)(), Int (Me::*)() const>(type, "AbiPtr", &Me::AbiPtr));
    	type->AddOperation(new BasicOperation<Int (Me::*)(), Int (Me::*)() const>(type, "Count", &Me::Count));
    	type->AddOperation(new BasicOperation<Int (Me::*)(), Int (Me::*)() const>(type, "Size", &Me::Size));

    	type->AddOperation(new BasicOperation<Typed* (Me::*)(), Type* (Me::*)()>(type, Type::MyType(), "GetValueType", &Me::GetValueType));
    	type->AddOperation(new BasicOperation<Typed* (Me::*)(), Typed* (Me::*)()>(type, Typed::MyType(), "CreateValueHolder", &Me::CreateValueHolder));
    }

private:

    void SetBits(void *mem, BigInt bits, Int idx, Int nbits, Int ptr);
    BigInt GetBits(const void *mem, Int idx, Int nbits, Int ptr) const;

    void GetValue(const void *mem, void *value, Int idx, Int ptr) const;
    void SetValue(void *mem, const void *value, Int idx, Int ptr);

    const void *BitmapPtr(const void *mem, Int ptr) const {
        return static_cast<const char*>(mem) + ptr;
    }

    void *BitmapPtr(void *mem, Int ptr) {
        return static_cast<char*>(mem) + ptr;
    }

    const void *ValuePtr(const void *mem, Int idx, Int ptr) const {
        return static_cast<const char*>(mem) + ptr + Offset() + idx * Size();
    }

    void *ValuePtr(void *mem, Int idx, Int ptr) {
        return static_cast<char*>(mem) + ptr + Offset() + idx * Size();
    }

    void SetupValueType();

    const Int  ptr_;
    const Int  abi_ptr_;
    const Int  offset_;
    const Int  limit_;
    const Int  item_size_;

protected:
    Type* valueType_;

    CUBEFS_DECLARE_REFLECTION;
};

template <typename Interface>
Type* FieldMetadataImplT<Interface>::type_ 					= NULL;

typedef FieldMetadataImplT<cubefs::api::FieldMetadata> 		FieldMetadataImpl;



template <typename Interface>
void FieldMetadataImplT<Interface>::SetValue(void *mem, const void *value, Int idx, Int ptr) {
    if (idx < Count()) {
        char *buffer = static_cast<char*> (mem) + ptr + Offset() + Size() * idx;
        const char *val = static_cast<const char*> (value);
        cubefs::tools::CopyBuffer(val, buffer, Size());
    } else {
        throw new BoundsException(CUBEFS_SOURCE, "ArrayField index is out of bounds.", idx, 0, Count());
    }
}

template <typename Interface>
void FieldMetadataImplT<Interface>::GetValue(const void *mem, void *value, Int idx, Int ptr) const {
    if (idx < Count()) {
        const char *buffer = (static_cast<const char*> (mem) + ptr + Offset() + Size() * idx);
        char *val = static_cast<char*> (value);
        cubefs::tools::CopyBuffer(buffer, val, Size());
    } else {
        throw new BoundsException(CUBEFS_SOURCE, "ArrayField index is out of bounds.", idx, 0, Count());
    }
}

template <typename Interface>
BigInt FieldMetadataImplT<Interface>::GetBits(const void *mem, Int idx, Int nbits, Int ptr) const {
    const void *tmp = static_cast<const char*> (mem) + ptr;
    const Int *buffer = static_cast<const Int*> (tmp);
    Int top_range = idx + Offset() + nbits;
    Int bit_size = top_range <= Limit() ? nbits : nbits - (top_range - Limit());
    return cubefs::tools::GetBits(buffer, idx, bit_size);
}

template <typename Interface>
void FieldMetadataImplT<Interface>::SetBits(void *mem, BigInt bits, Int idx, Int nbits, Int ptr) {
    void *tmp = static_cast<char*> (mem) + ptr;
    Int *buffer = static_cast<Int*> (tmp);
    Int top_range = idx + Offset() + nbits;
    Int bit_size = top_range <= Limit() ? nbits : nbits - (top_range - Limit());
    cubefs::tools::SetBits(buffer, idx, Offset(), bit_size);
}



struct CreateValueHolderFn {
    Typed* value_;

    template <typename T>
    void operator()(T* dummy) {
        //value_ = new typename TypedWrapperFactory<T>::Type();
    }
};

template <typename Interface>
Typed* FieldMetadataImplT<Interface>::CreateValueHolder() {
    CreateValueHolderFn fn;

    TypeDispatcher<Code2TypeMap>::Dispatch(Base::GetTypeCode(), (void*)NULL, fn);

    return fn.value_;
}


struct SetupValueTypeFn {
    Type* valueType_;

    template <typename T>
    void operator()(T* dummy) {
        valueType_ = TypeFactory<typename TypedWrapperFactory<T>::Type>::type();
    }
};

template <typename Interface>
void FieldMetadataImplT<Interface>::SetupValueType() {
    SetupValueTypeFn fn;

    Int type_code = Base::GetTypeCode();

    if (type_code >= 0 && type_code < 8) {
        TypeDispatcher<Code2TypeMap>::Dispatch(type_code, (void*)NULL, fn);
    }

    this->valueType_ = fn.valueType_;
}

}}

#undef CUBEFS_SOURCE
#endif
